#include <stdlib.h>
//#include <stdio.h>
//#include <string.h>

#include "tuya_ble_port.h"
#include "tuya_ble_common.h" 
//#include "tuya_ble_internal_config.h"
#include "tuya_ble_log.h"

#include "aes.h"
#include "md5.h" //tbd

/*
void TUYA_BLE_LOG(const char *format,…)
{
    dbg_printf(format, __VA_ARGS__)；
}
*/
void TUYA_BLE_HEXDUMP(uint8_t *p_data , uint16_t len)
{
    uint16_t i=0;
    for(i=0;i<len;i++)
		dbg_printf("%02x", p_data[i]);
	dbg_printf("\n");
}

tuya_ble_status_t tuya_ble_gap_advertising_adv_data_update(uint8_t const* p_ad_data, uint8_t ad_len)
{
    return (tuya_ble_status_t)gap_adv_data_update(p_ad_data, ad_len);
}


tuya_ble_status_t tuya_ble_gap_advertising_scan_rsp_data_update(uint8_t const *p_sr_data, uint8_t sr_len)
{
    return (tuya_ble_status_t)gap_rsp_data_update(p_sr_data, sr_len);
}



tuya_ble_status_t tuya_ble_gap_disconnect(void)
{
    // ble_device_disconnected();
//	if(blc_ll_getCurrentState()==BLS_LINK_STATE_CONN)
//	{
//		bls_ll_terminateConnection(HCI_ERR_REMOTE_USER_TERM_CONN);
//	}

    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_gatt_send_data(const uint8_t *p_data,uint16_t len)
{
	  attHandleValueNoti_t notiHdl={0};
		notiHdl.len = len;
	  uint16_t con_hdl = 0;
		//TUYA_APP_LOG_INFO("N_data..");
    if(len > TUYA_BLE_DATA_MTU_MAX)
    {
        notiHdl.len = TUYA_BLE_DATA_MTU_MAX;
    }
		osal_memcpy(notiHdl.value, p_data, notiHdl.len);  //tuya_ble_connect_status_get()!=BONDING_CONN
//		if(con_hdl != 0xff)
//		{
			  return (tuya_ble_status_t)tuya_gen_Notify(con_hdl, &notiHdl);
//		}
//    else
//    {
//        return TUYA_BLE_ERR_INVALID_STATE;
//    }
#if 0
	if(bls_ll_isConnectState())
	{
		return bls_att_pushNotifyData(0x11, p_data, len);
	}
    else
    {
        return TUYA_BLE_ERR_INVALID_STATE;
    }
#endif
}

// skip these functions,using osal_start/stop/reload_timerEx() functions instead, to follow the OSAL style.
#if 0
tuya_ble_status_t tuya_ble_timer_create(void** p_timer_id,uint32_t timeout_value_ms, tuya_ble_timer_mode mode,tuya_ble_timer_handler_t timeout_handler)
{
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_timer_delete(void* timer_id)
{
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_timer_start(void* timer_id)
{
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_timer_restart(void * timer_id, uint32_t timeout_value_ms)
{
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_timer_stop(void* timer_id)
{
    return TUYA_BLE_SUCCESS;
}
#endif

void tuya_ble_device_delay_ms(uint32_t ms)
{
    WaitMs(ms);
}

void tuya_ble_device_delay_us(uint32_t us)
{
    WaitUs(us);
}

tuya_ble_status_t tuya_ble_device_reset(void)
{
    hal_system_soft_reset();
    return TUYA_BLE_SUCCESS;
}

//tuya_ble_status_t tuya_ble_gap_address_get(uint8_t mac[6])
tuya_ble_status_t tuya_ble_gap_addr_get(tuya_ble_gap_addr_t *p_addr)
{
    // ll.c: ownPublicAddr   0x1fff1231  data  6
    // This address need to be updated in case it's changed.
    volatile uint8_t* p_ownPublicAddr = (volatile uint8_t*)0x1fff0965;  // It's 0x1fff0965 for 6222.
//    p_addr->addr[5] = *(p_ownPublicAddr++);
//    p_addr->addr[4] = *(p_ownPublicAddr++);
//    p_addr->addr[3] = *(p_ownPublicAddr++);
//    p_addr->addr[2] = *(p_ownPublicAddr++);
//    p_addr->addr[1] = *(p_ownPublicAddr++);
//    p_addr->addr[0] = *(p_ownPublicAddr++);
    p_addr->addr[0] = *(p_ownPublicAddr++);
    p_addr->addr[1] = *(p_ownPublicAddr++);
    p_addr->addr[2] = *(p_ownPublicAddr++);
    p_addr->addr[3] = *(p_ownPublicAddr++);
    p_addr->addr[4] = *(p_ownPublicAddr++);
    p_addr->addr[5] = *(p_ownPublicAddr++);
	  p_addr->addr_type = TUYA_BLE_ADDRESS_TYPE_RANDOM; // TUYA_BLE_ADDRESS_TYPE_RANDOM /TUYA_BLE_ADDRESS_TYPE_PUBLIC
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_gap_addr_set(tuya_ble_gap_addr_t *p_addr)
{
    // ll.c: ownPublicAddr   0x1fff1231  data  6
    // This address need to be updated in case it's changed.
    volatile uint8_t* p_ownPublicAddr = (volatile uint8_t*)0x1fff0965;  // It's 0x1fff0965 for 6222.
//    *(p_ownPublicAddr++) = p_addr->addr[5];
//    *(p_ownPublicAddr++) = p_addr->addr[4];
//    *(p_ownPublicAddr++) = p_addr->addr[3];
//    *(p_ownPublicAddr++) = p_addr->addr[2];
//    *(p_ownPublicAddr++) = p_addr->addr[1];
//    *(p_ownPublicAddr++) = p_addr->addr[0];
    *(p_ownPublicAddr++) = p_addr->addr[0];
    *(p_ownPublicAddr++) = p_addr->addr[1];
    *(p_ownPublicAddr++) = p_addr->addr[2];
    *(p_ownPublicAddr++) = p_addr->addr[3];
    *(p_ownPublicAddr++) = p_addr->addr[4];
    *(p_ownPublicAddr++) = p_addr->addr[5];
    return TUYA_BLE_SUCCESS;
}

void tuya_ble_device_enter_critical(void)
{
    HAL_ENTER_CRITICAL_SECTION();
}

void tuya_ble_device_exit_critical(void)
{
    HAL_EXIT_CRITICAL_SECTION();
}

tuya_ble_status_t tuya_ble_rand_generator(uint8_t* p_buf, uint8_t len)
{

     for(uint32_t i=0; i<len; i++)
    {
    	 if(i==0) p_buf[i]=5;
    	 else
    	 {
    		 p_buf[i] = rand();
    	 }
    }
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_rtc_get_timestamp(uint32_t *timestamp,int32_t *timezone)
{
    *timestamp = 0;
    *timezone = 0;
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_rtc_set_timestamp(uint32_t timestamp,int32_t timezone)
{

    return TUYA_BLE_SUCCESS;
}


tuya_ble_status_t tuya_ble_nv_init(void)
{
    xflash_Ctx_t cfg =
    {
        .spif_ref_clk   =   SYS_CLK_DLL_64M,
        .rd_instr       =   XFRD_FCMD_READ_DUAL
    };
    hal_spif_cache_init(cfg);
    return TUYA_BLE_SUCCESS;
}

tuya_ble_status_t tuya_ble_nv_erase(uint32_t addr,uint32_t size)
{
    tuya_ble_status_t result = TUYA_BLE_SUCCESS;

    uint32_t erase_pages, i;

    /* make sure the start address is a multiple of FLASH_ERASE_MIN_SIZE */
    if(addr % TUYA_NV_ERASE_MIN_SIZE != 0)
    {
       // app_log_d("the start address is a not multiple of TUYA_NV_ERASE_MIN_SIZE");
        return TUYA_BLE_ERR_INVALID_ADDR;
    }

    /* calculate pages */
    erase_pages = size / TUYA_NV_ERASE_MIN_SIZE;
    if (size % TUYA_NV_ERASE_MIN_SIZE != 0) {
        erase_pages++;
    }

    /* start erase */
    for (i = 0; i < erase_pages; i++)
	{
		//if(nrf_fstorage_port_erase_sector(addr + (4096 * i),true)!=0)
        hal_flash_erase_sector(addr + (TUYA_NV_ERASE_MIN_SIZE * i));
    }
    return result;
}

tuya_ble_status_t tuya_ble_nv_write(uint32_t addr,const uint8_t *p_data, uint32_t size)
{
    return (tuya_ble_status_t)hal_flash_write(addr,(uint8_t *)p_data,size); //TUYA_BLE_SUCCESS == PPlus_SUCCESS == 0
}


tuya_ble_status_t tuya_ble_nv_read(uint32_t addr,uint8_t *p_data, uint32_t size)
{
    return (tuya_ble_status_t)hal_flash_read(addr,p_data,size); //TUYA_BLE_SUCCESS == PPlus_SUCCESS == 0
}

tuya_ble_status_t tuya_ble_common_uart_send_data(const uint8_t *p_data,uint16_t len)
{
	  //uart_send_data (p_data,len);
	  UART_INDEX_e idx=UART0; // tbd
	  hal_uart_send_buff(idx,(uint8_t *)p_data,len);
    return TUYA_BLE_SUCCESS;
}

// If uart would be initialized before calling tuya_ble_sdk_init(), then porting of this function can be skipped
tuya_ble_status_t tuya_ble_common_uart_init(void)
{
    return TUYA_BLE_SUCCESS;
}

#if TUYA_BLE_USE_OS
bool tuya_ble_os_task_create(void **pp_handle, const char *p_name, void (*p_routine)(void *),void *p_param, uint16_t stack_size, uint16_t priority)
{
    return os_task_create(pp_handle, p_name, p_routine,p_param, stack_size, priority);
}

bool tuya_ble_os_task_delete(void *p_handle)
{
    return os_task_delete(p_handle);
}

bool tuya_ble_os_task_suspend(void *p_handle)
{
    return os_task_suspend(p_handle);
}

bool tuya_ble_os_task_resume(void *p_handle)
{
    return os_task_resume(p_handle);
}

bool tuya_ble_os_msg_queue_create(void **pp_handle, uint32_t msg_num, uint32_t msg_size)
{
    return os_msg_queue_create(pp_handle, msg_num, msg_size);
}

bool tuya_ble_os_msg_queue_delete(void *p_handle)
{
    return os_msg_queue_delete(p_handle);
}

bool tuya_ble_os_msg_queue_peek(void *p_handle, uint32_t *p_msg_num)
{
    return os_msg_queue_peek(p_handle, p_msg_num);
}

bool tuya_ble_os_msg_queue_send(void *p_handle, void *p_msg, uint32_t wait_ms)
{
    return os_msg_send(p_handle, p_msg, wait_ms);
}

bool tuya_ble_os_msg_queue_recv(void *p_handle, void *p_msg, uint32_t wait_ms)
{
    return os_msg_recv(p_handle, p_msg, wait_ms);
}

#endif

#if 0 
bool tuya_ble_aes128_ecb_encrypt(uint8_t *key,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    uint16_t length;
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    length = input_len;

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_enc(&aes_ctx, key, 128);

    while( length > 0 )
    {
        mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, input, output );
        input  += 16;
        output += 16;
        length -= 16;
    }

    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_ecb_decrypt(uint8_t *key,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    uint16_t length;
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    length = input_len;

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_dec(&aes_ctx, key, 128);

    while( length > 0 )
    {
        mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_DECRYPT, input, output );
        input  += 16;
        output += 16;
        length -= 16;
    }

    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_cbc_encrypt(uint8_t *key,uint8_t *iv,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_enc(&aes_ctx, key, 128);

    mbedtls_aes_crypt_cbc(&aes_ctx,MBEDTLS_AES_ENCRYPT,input_len,iv,input,output);
    //
    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_cbc_decrypt(uint8_t *key,uint8_t *iv,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_dec(&aes_ctx, key, 128);

    mbedtls_aes_crypt_cbc(&aes_ctx,MBEDTLS_AES_DECRYPT,input_len,iv,input,output);
    //
    mbedtls_aes_free(&aes_ctx);

    return true;
}


bool tuya_ble_md5_crypt(uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_md5_context md5_ctx;
    mbedtls_md5_init(&md5_ctx);
    mbedtls_md5_starts(&md5_ctx);
    mbedtls_md5_update(&md5_ctx, input, input_len);
    mbedtls_md5_finish(&md5_ctx, output);
    mbedtls_md5_free(&md5_ctx);
	//tuya_log_dumpHex("md5-2:",100,output,16);
    return true;
}
#endif

bool tuya_ble_aes128_ecb_encrypt(uint8_t *key,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    uint16_t length;
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    length = input_len;

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_enc(&aes_ctx, key, 128);

    while( length > 0 )
    {
        mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_ENCRYPT, input, output );
        input  += 16;
        output += 16;
        length -= 16;
    }

    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_ecb_decrypt(uint8_t *key,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    uint16_t length;
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    length = input_len;

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_dec(&aes_ctx, key, 128);

    while( length > 0 )
    {
        mbedtls_aes_crypt_ecb(&aes_ctx, MBEDTLS_AES_DECRYPT, input, output );
        input  += 16;
        output += 16;
        length -= 16;
    }

    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_cbc_encrypt(uint8_t *key,uint8_t *iv,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_enc(&aes_ctx, key, 128);
    
    mbedtls_aes_crypt_cbc(&aes_ctx,MBEDTLS_AES_ENCRYPT,input_len,iv,input,output);
    //
    mbedtls_aes_free(&aes_ctx);

    return true;
}

bool tuya_ble_aes128_cbc_decrypt(uint8_t *key,uint8_t *iv,uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_aes_context aes_ctx;
    //
    if(input_len%16)
    {
        return false;
    }

    mbedtls_aes_init(&aes_ctx);

    mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
    
    mbedtls_aes_crypt_cbc(&aes_ctx,MBEDTLS_AES_DECRYPT,input_len,iv,input,output);
    //
    mbedtls_aes_free(&aes_ctx);

    return true;
}


bool tuya_ble_md5_crypt(uint8_t *input,uint16_t input_len,uint8_t *output)
{
    mbedtls_md5_context md5_ctx;
    mbedtls_md5_init(&md5_ctx);
    mbedtls_md5_starts(&md5_ctx);
    mbedtls_md5_update(&md5_ctx, input, input_len);
    mbedtls_md5_finish(&md5_ctx, output);
    mbedtls_md5_free(&md5_ctx);    
    
    return true;
}

bool tuya_ble_hmac_sha1_crypt(const uint8_t *key, uint32_t key_len, const uint8_t *input, uint32_t input_len, uint8_t *output)
{
	return true;
}

bool tuya_ble_hmac_sha256_crypt(const uint8_t *key, uint32_t key_len, const uint8_t *input, uint32_t input_len, uint8_t *output)
{
	return true;
}


// not supported
#if 0
bool tuya_ble_hmac_sha1_crypt(const uint8_t *key, uint32_t key_len, const uint8_t *input, uint32_t input_len, uint8_t *output)
{
    return true;
}

bool tuya_ble_hmac_sha256_crypt(const uint8_t *key, uint32_t key_len,const uint8_t *input, uint32_t input_len, uint8_t *output)
{
    return true;
}
#endif

// not use it at the moment
#if (TUYA_BLE_USE_PLATFORM_MEMORY_HEAP==1)
void *tuya_ble_port_malloc( uint32_t size )
{
  if(size > 0xffff) // osal_mem_alloc() works with 16-bit size.
		  return NULL;
	else
      return osal_mem_alloc((uint16_t)size);
}

void tuya_ble_port_free( void *pv )
{
    osal_mem_free(pv);
}

#endif

#if 0
void* ty_malloc(uint16_t size)
{
	extern void *tuya_ble_malloc(uint16_t size);
	return tuya_ble_malloc(size);
}

void ty_free(void* ptr)
{
	extern void tuya_ble_free(ptr);
	tuya_ble_free(ptr);
}

u32 tuya_bsp_flash_read_bytes(uint32_t addr,uint8_t *p_data, uint32_t size)
{
	return tuya_ble_nv_read(addr,p_data, size);
}

u32 tuya_bsp_flash_write_bytes(uint32_t addr,uint8_t *p_data, uint32_t size)
{
	return tuya_ble_nv_write(addr,p_data, size);
}
u32 tuya_bsp_flash_erease_sector(uint32_t addr)
{
	tuya_ble_nv_erase(addr,4096);
	return 0;
}

u32 tuya_bsp_uart_send_bytes(u8 * data,u16 len)
{
	return uart_send_data (data,len);
}

u32 tuya_bsp_log_send_bytes(u8 * data,u16 len)
{
	extern u32 log_send_bytes(u8*data,u16 data_len);
	return log_send_bytes(data,len);
}
#endif

